/* 
 * File:   PointsAdapter.cpp
 * Author: Guilherme Monteiro
 */

#include <vector>

#include "../headers/GenericAdapter.h"

GenericAdapter::GenericAdapter(AdaptationType type, MetaInfo* meta) throw (NullImageException) {
    setAdaptationType(type);
    setMetaInfo(meta);
    configureAdapter();
}

GenericAdapter::GenericAdapter(const GenericAdapter& orig) {
}

GenericAdapter::~GenericAdapter() {
}

/**
 * Configure the taxes of adaptation that
 * depends of the adaptation type.
 */
void GenericAdapter::configureAdapter() throw (NullImageException) {
//    printf("meta info low: %s\n", metaInfo->getLowResolutionImagePath().c_str());
//    printf("meta info high: %s\n", metaInfo->getHighResolutionImagePath().c_str());
    
    IplImage* lowResolutionImage = cvLoadImage(metaInfo->getLowResolutionImagePath().c_str());
    IplImage* highResolutionImage = cvLoadImage(metaInfo->getHighResolutionImagePath().c_str());

    if(lowResolutionImage == 0) {
        throw NullImageException(metaInfo->getLowResolutionImagePath());
    }
    else if(highResolutionImage == 0) {
        throw NullImageException(metaInfo->getHighResolutionImagePath());
    }
    
//    cvShowImage("low image", lowResolutionImage);
//    cvNamedWindow("low image", CV_WINDOW_FREERATIO);
//    
//    cvShowImage("high image", highResolutionImage);
//    cvNamedWindow("high image", CV_WINDOW_FREERATIO);
//    cvWaitKey(0);
    
    int lowHeight = lowResolutionImage->height;
    int lowWidth = lowResolutionImage->width;
    int highHeight = highResolutionImage->height;
    int highWidth = highResolutionImage->width;
    
//    printf("\nlowHeight: %d\n", lowHeight);
//    printf("lowWidth: %d\n", lowWidth);
//    printf("highHeight: %d\n", highHeight);
//    printf("highWidth: %d\n", highWidth);
    
    cvReleaseImage(&lowResolutionImage);
    cvReleaseImage(&highResolutionImage);
    
    if (getAdaptationType() == HIGH_2_LOW) {
        adaptationHorizontalTax = (double)lowWidth/highWidth;
        adaptationVerticalTax = (double)lowHeight/highHeight;
    }
    else if(getAdaptationType() == LOW_2_HIGH) {
        adaptationHorizontalTax = (double)highWidth/lowWidth;
        adaptationVerticalTax = (double)highHeight/lowHeight;
    }
}

/**
 * Adapt point according
 * the way type of adaptation.
 * The adaptation is stable, i.e.,
 * the initial order of points don't change.
 * 
 * @return vector of points adapted
 */
vector<CvPoint> GenericAdapter::adaptPoints(vector<CvPoint> vec) {
    vector<CvPoint> adaptedPoints;

    vector<CvPoint>::iterator pointsIt;
    for(pointsIt = vec.begin(); pointsIt != vec.end(); pointsIt++) {
        adaptedPoints.push_back(adaptPoint(*pointsIt));
    }
    
    return adaptedPoints;
}

/**
 * Adapt one point according the
 * adaptation type.
 * 
 * @param point
 * @return 
 */
CvPoint GenericAdapter::adaptPoint(CvPoint point) {
    CvPoint newPoint;
    newPoint.x = floor(point.x * adaptationHorizontalTax);
    newPoint.y = floor(point.y * adaptationVerticalTax);
    
    return newPoint;
}

/**
 * Adapt the cells according
 * the type of adaptation.
 * The adaptation is stable, i.e.,
 * the initial order of points don't change.
 * 
 * @param cells: cells in high or low resolution
 * @return vector of cells adapted
 */
vector<Cell> GenericAdapter::adaptCells(vector<Cell> cells) {
    vector<CvPoint> pointsAdapted = adaptPoints(transformCellsToPoints(cells));
    vector<Cell> cellsAdapted;
    
    vector<CvPoint>::iterator itPoints;
    for(itPoints = pointsAdapted.begin(); itPoints != pointsAdapted.end(); itPoints++) {
        CvPoint c1 = *itPoints;
        CvPoint c2 = *(++itPoints); 
        
        Cell cell(c1, c2);
        cellsAdapted.push_back(cell);
    }
    
    return cellsAdapted;
}

/**
 * Transform a vector of
 * cells in a vector of points,
 * the order is preserved.
 * 
 * @param lines
 * @return vector of points
 */
vector<CvPoint> GenericAdapter::transformCellsToPoints(vector<Cell> cells) {
    vector<CvPoint> points;
    vector<Cell>::iterator it;
    for(it = cells.begin(); it < cells.end(); it++) {
        points.push_back((*it).leftUpper);
        points.push_back((*it).rightBottom);
    }
    return points;
}

/**
 * Configures the adaptation type.
 * 
 * @param type
 */
void GenericAdapter::setAdaptationType(AdaptationType type) {
    this->adaptationType = type;
}

/**
 * Returns the adaptation type.
 * 
 * @return adaptationType
 */
AdaptationType GenericAdapter::getAdaptationType() {
    return this->adaptationType;
}

/**
 * Configures the metainfo.
 * 
 * @param meta
 */
void GenericAdapter::setMetaInfo(MetaInfo* meta) {
    this->metaInfo = meta;
}

/**
 * Return the metainfo.
 * 
 * @return metainfo
 */
MetaInfo* GenericAdapter::getMetaInfo() {
    return this->metaInfo;
}



